/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.impl;

import java.awt.geom.Point2D;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import who.tmap.bo.AttrDef;
import who.tmap.bo.Attrs;
import who.tmap.bo.cd.CartographicDisplay;
import who.tmap.bo.cd.point.CDPoint;
import who.tmap.common.TMapException;
import who.tmap.toolkit.MappingToolKit;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.PolygonShape;

public class CartographicDisplayHelper {
	
    private static Logger logger = Logger.getLogger(CartographicDisplayHelper.class); 

    /**
     * Assign a value to a map shape
     * @param mtk
     * @param attrs
     * @param cd
     * @return
     * @throws TMapException
     */
	public Map<MapShape, Object> assignValues( MappingToolKit mtk, Attrs attrs, CartographicDisplay cd ) throws TMapException {
		return assignValues( mtk, attrs, cd, false );
	}
    
	/**
	 * 
	 * @param mtk
	 * @param attrs
	 * @param cd
	 * @return
	 * @throws TMapException
	 */
	public Map<MapShape, Object> assignAllValues( MappingToolKit mtk, Attrs attrs, CartographicDisplay cd ) throws TMapException {	
		return assignValues( mtk, attrs, cd, true );
	}

	/**
	 * @param labelColumnName
	 * @param defs
	 * @param idColumnName
	 * @return
	 */
	private String retrieveLabelColumnName( String labelColumnName, List<AttrDef> defs ) {//, String idColumnName ) {
		for( AttrDef def : defs ) {
			if ( def.getName().equalsIgnoreCase( labelColumnName ) ) {
				return labelColumnName;
			}
		}
		
		return null;//idColumnName;
	}
    
    /**
     * Assign a value to a map shape
     * @param mtk
     * @param attrs
     * @param cd
     * @param returnAllValues if true, returns all data or a value from the data table
     * @return 
     * @throws TMapException
     */
	@SuppressWarnings("unchecked")
	public Map<MapShape, Object> assignValues( MappingToolKit mtk, Attrs attrs, CartographicDisplay cd, boolean returnAllValues ) throws TMapException {
		// Point cartographic display is not linked to the map by the polygon names
		// 
		if ( cd instanceof CDPoint ) {
			return null;
		}
		// ID -> N polygons
		Map<String,List<PolygonShape>> polygons =new TreeMap<String,List<PolygonShape>>( String.CASE_INSENSITIVE_ORDER );
		
		String idColumn =cd.getJoinIdColumnName();
		String dataColumn =cd.getDataColumnName();

		Map<MapShape, Object> cdShapes =new HashMap<MapShape, Object>();
		
		if ( attrs == null || idColumn == null || dataColumn == null ) {
			return cdShapes;
		}	
		
		String labelColumn =retrieveLabelColumnName( mtk.getThematicMap().getAnnotations().getLabelColumnName()
												   , attrs.getHeader() );
												   //, idColumn );
	
		for( MapShape mapShape : mtk.getShapes() ) {			
			if ( mapShape instanceof PolygonShape ) {
				PolygonShape polygon =(PolygonShape)mapShape;
				
				if ( polygon.getName() != null ) {
					// Because there can be several polygons with the same name,
					// we group them in a list
					List<PolygonShape> sameNamePolygons =(List<PolygonShape>)polygons.get( polygon.getName() );
					if ( sameNamePolygons == null ) {
						sameNamePolygons =new ArrayList<PolygonShape>();
						polygons.put( polygon.getName(), sameNamePolygons );
					}
					
					sameNamePolygons.add( polygon );
				}
			}
		}
		
		// Look for attributes column indexes
		int idColIdx =-1, dataColIdx =-1, labelColIdx =-1;
		boolean labelTypeNumeric =false;
		List<AttrDef> headers =(List<AttrDef>)attrs.getHeader();
		
		for( int ct =0; ct < headers.size(); ct++ ) {
			AttrDef attrDef =(AttrDef)headers.get( ct );
			
			if ( idColumn.equalsIgnoreCase( attrDef.getName() ) ) {
				idColIdx =ct;
			}
			
			if ( dataColumn.equalsIgnoreCase( attrDef.getName() ) ) {
				dataColIdx =ct;
			}
			
			if ( labelColumn != null && labelColumn.equalsIgnoreCase( attrDef.getName() ) ) {
				labelColIdx =ct;
				String labelType =attrDef.getType();
				if ( labelType != null && !"".equals( labelType )
						&& !String.class.getName().equals( labelType ) 
						&& !Boolean.class.getName().equals( labelType ) ) {
					labelTypeNumeric =true;
				}
			}			
		}
		
		if ( idColIdx == -1 ) {
			throw new TMapException( "Cannot find attributes column [" + idColumn + "]" );
		}
		
		if ( dataColIdx == -1 ) {
			throw new TMapException( "Cannot find attributes column [" + dataColumn + "]" );
		}
		
		if ( labelColumn != null && labelColIdx == -1 ) {
			throw new TMapException( "Cannot find attributes column [" + labelColumn + "]" );
		}		
		
		attrs.setJoinIdIdx( idColIdx );
		attrs.setDataIdx( dataColIdx );
		
		NumberFormat formatter = NumberFormat.getNumberInstance();

		List rows =(List)attrs.getRows();

		for( int ct =0; ct < rows.size(); ct++ ) {
			List row =(List)rows.get( ct );

			Object keyObj =row.get( idColIdx );
			if ( keyObj != null ) {
				// joinId
				String key =row.get( idColIdx ).toString();
				// value
				Object val =row.get( dataColIdx );
				// displayed name inside the polygon
				Object labelObj =labelColIdx != -1 ? row.get( labelColIdx ) : null;
				
				String label =null;
				
				if ( labelTypeNumeric ) {
					label =formatter.format( labelObj );
				} else if ( labelObj != null ){
					label =labelObj.toString();
				}
			
				List<PolygonShape> sameNamePolygons =(List<PolygonShape>)polygons.get( key );
				if ( sameNamePolygons != null ) {
					for( PolygonShape polygon : sameNamePolygons ) {					
						cdShapes.put( polygon, returnAllValues ? row : val );
						
						polygon.setLabelText( label );
					}
				}
			}
			
		}		
		
		return cdShapes;
	}
	
	@SuppressWarnings("unchecked")
	public String getDescForCDPoint( Point2D pt, Attrs data ) {
		String desc ="";
		
		if ( data != null && data.getXColumnIdx() != -1 
		          && data.getYColumnIdx() != -1 && data.getDescriptorColumnIdx() != -1 ) {
			List<List> rows =data.getRows();
			if ( rows != null ) {
				for( List row : rows ) {
					if ( ( pt.getX() == (Double)row.get( data.getXColumnIdx() ) ) 
					  && ( -pt.getY() == (Double)row.get( data.getYColumnIdx() ) ) ) {
						desc =(String)row.get( data.getDescriptorColumnIdx() );
						break;
					}
				}
			}
		}
		
		return desc;
	}
	
	/**
	 * For CDPointDataDrivenSymbol & CDPointDataDrivenSymbolAndSize
	 * @param pt
	 * @param data
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public String getDataForCDPoint( Point2D pt, Attrs data ) {
		String dataVal ="";
		
		if ( data != null && data.getXColumnIdx() != -1 
		          && data.getYColumnIdx() != -1 && data.getDataIdx() != -1 ) {
			List<List> rows =data.getRows();
			if ( rows != null ) {
				for( List row : rows ) {
					if ( ( pt.getX() == (Double)row.get( data.getXColumnIdx() ) ) 
					  && ( -pt.getY() == (Double)row.get( data.getYColumnIdx() ) ) ) {
						dataVal =row.get( data.getDataIdx() ).toString();
						break;
					}
				}
			}
		}
		
		return dataVal;
	}	

}
